package com.sdhs.framework.config;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.TimeZone;

import cn.hutool.core.net.NetUtil;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import org.apache.ibatis.reflection.MetaObject;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.expression.ParseException;
import org.springframework.format.Formatter;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JSR310Module;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.sdhs.common.mybatis.PopulateTableIdInterceptor;
import com.sdhs.common.utils.SecurityUtils;

/**
 * 程序注解配置
 *
 * @author engineering
 */
@SuppressWarnings("deprecation")
@Configuration
// 表示通过aop框架暴露该代理对象,AopContext能够访问
@EnableAspectJAutoProxy(exposeProxy = true)
// 指定要扫描的Mapper类的包的路径
@MapperScan("com.sdhs.**.mapper")
public class ApplicationConfig
{
    /**
     * 时区配置
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization()
    {
        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
    }

    @Bean
    public Formatter<LocalDate> localDateFormatter() {
        return new Formatter<LocalDate>() {

            @Override
            public LocalDate parse(String text, Locale locale) throws ParseException {
                return LocalDate.parse(text, DateTimeFormatter.ISO_LOCAL_DATE);
            }

            @Override
            public String print(LocalDate object, Locale locale) {
                return DateTimeFormatter.ISO_LOCAL_DATE.format(object);
            }
        };
    }

    @Bean
    public Formatter<LocalDateTime> localDateTimeFormatter(@Value("${spring.jackson.date-format}") String dateFormat) {
        return new Formatter<LocalDateTime>() {

            private DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);

            @Override
            public LocalDateTime parse(String text, Locale locale) throws ParseException {
                return LocalDateTime.parse(text, formatter);
            }

            @Override
            public String print(LocalDateTime object, Locale locale) {
                return formatter.format(object);
            }
        };
    }

    @Bean
    public BeanPostProcessor objectMapperProcessor(@Value("${spring.jackson.date-format}") String dateFormat) {
        return new BeanPostProcessor() {

            @Override
            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                if (!(bean instanceof ObjectMapper)) {
                    return bean;
                }
                ObjectMapper om = (ObjectMapper) bean;
                List<Module> modules = ObjectMapper.findModules();
                JSR310Module jsr310Module;
                Optional<Module> optional3 = modules.stream().filter(module -> module instanceof JSR310Module)
                        .findAny();
                if (optional3.isPresent()) {
                    jsr310Module = (JSR310Module) optional3.get();
                } else {
                    jsr310Module = new JSR310Module();
                    modules.add(jsr310Module);
                }
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);
                jsr310Module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
                jsr310Module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
                om.registerModules(modules);
                om.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
                        .setSerializationInclusion(Include.NON_NULL);
                return om;
            }

            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                return bean;
            }
        };
    }

    /**
     * 新增分页拦截器，并设置数据库类型为mysql
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));
        return interceptor;
    }

    @Bean
    public MetaObjectHandler metaObjectHandler() {
        return new MetaObjectHandler() {

            @Override
            public void insertFill(MetaObject metaObject) {
                this.setFieldValByName("createBy", SecurityUtils.getUsername(), metaObject);
                this.setFieldValByName("createTime", new Date(), metaObject);
                this.setFieldValByName("updateBy", SecurityUtils.getUsername(), metaObject);
                this.setFieldValByName("updateTime", new Date(), metaObject);
            }

            @Override
            public void updateFill(MetaObject metaObject) {
                this.setFieldValByName("updateBy", SecurityUtils.getUsername(), metaObject);
                this.setFieldValByName("updateTime", new Date(), metaObject);
            }
        };
    }

    @Bean
    public PopulateTableIdInterceptor populateTableIdInterceptor() {
    	return new PopulateTableIdInterceptor();
    }

    /**
     * 使用网卡信息绑定雪花生成器
     * 防止集群雪花ID重复
     */
    @Bean
    public IdentifierGenerator idGenerator() {
        return new DefaultIdentifierGenerator(NetUtil.getLocalhost());
    }
}
